﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Windows.Controls;
using ShredTheLog.Api;
using System.Diagnostics.Contracts;
using System.Collections;
using System.Windows.Data;
using System.ComponentModel;

namespace ShredTheLog.Controls
{
    public static class Extensions
    {
        public static bool? GetIsWindowClosed(DependencyObject obj)
        {
            return (bool?)obj.GetValue(IsWindowClosedProperty);
        }
        public static void SetIsWindowClosed(DependencyObject obj, bool? value)
        {
            obj.SetValue(IsWindowClosedProperty, value);
        }
        public static readonly DependencyProperty IsWindowClosedProperty = 
            DependencyProperty.RegisterAttached("IsWindowClosed", typeof(bool?), typeof(Extensions), new PropertyMetadata(IsWindowClosedChangedCallback));
        private static void IsWindowClosedChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as Window;
            if (window != null)
            {
                EventHandler onWindowClosed = null;
                onWindowClosed = (sender2, e2) => SetIsWindowClosed(window, true);
                window.Closed += onWindowClosed;
                onWindowClosedHandlers.TryAdd(window, onWindowClosed);
                bool? newValue = (bool?)e.NewValue;
                if (newValue.HasValue && newValue.Value)
                {
                    EventHandler h;
                    if (onWindowClosedHandlers.TryGetValue(window, out h))
                    {
                        window.Closed -= h;
                        onWindowClosedHandlers.TryRemove(window, out h);
                    }
                    window.Close();
                }
            }
        }
        private static ConcurrentDictionary<Window, EventHandler> onWindowClosedHandlers = new ConcurrentDictionary<Window, EventHandler>();



        public static IEnumerable<object> GetItems(DependencyObject obj)
        {
            return (IEnumerable<object>)obj.GetValue(ItemsProperty);
        }
        public static void SetItems(DependencyObject obj, IEnumerable<object> value)
        {
            obj.SetValue(ItemsProperty, value);
        }
        public static readonly DependencyProperty ItemsProperty =
            DependencyProperty.RegisterAttached("Items", typeof(IEnumerable<object>), typeof(Extensions), new PropertyMetadata(ItemsChangedCallback));
        private static void ItemsChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as ItemsControl;
            if (sender != null)
            {
                sender.Items.Clear();
                if (e.NewValue != null)
                    ((IEnumerable<object>)e.NewValue).ForEach(i => sender.Items.Add(i));
            }
        }



        public static bool GetUnbindDocumentWhenSettingsUpdated(DependencyObject obj)
        {
            return (bool)obj.GetValue(UnbindDocumentWhenSettingsUpdatedProperty);
        }
        public static void SetUnbindDocumentWhenSettingsUpdated(DependencyObject obj, bool value)
        {
            obj.SetValue(UnbindDocumentWhenSettingsUpdatedProperty, value);
        }
        public static readonly DependencyProperty UnbindDocumentWhenSettingsUpdatedProperty =
            DependencyProperty.RegisterAttached("UnbindDocumentWhenSettingsUpdated", typeof(bool), typeof(Extensions), new PropertyMetadata(UnbindDocumentWhenSettingsUpdatedChangedCallback));
        private static void UnbindDocumentWhenSettingsUpdatedChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // По какой-то причине Document у FlowDocumentScrollViewer не отвязывается сам по себе при переключении настроек (или не всегда отвязывается).
            // Это может вызывать ошибку при приходе нового сообщения в чат-комнату сразу после переключения настроек.
            // Приходится отвязывать вручную :(
            var sender = d as FlowDocumentScrollViewer;
            if (sender != null && (bool)e.NewValue)
            {
                var shredServices = Globals.CompositionContainer.GetExportedValue<IShredServices>();
                EventHandler handler = null;
                handler = 
                    (sender1, e1) =>
                    {
                        shredServices.SettingsUpdated -= handler;
                        sender.Document = null;
                    };
                shredServices.SettingsUpdated += handler;
            }
        }



        public static string GetPassword(DependencyObject obj)
        {
            return (string)obj.GetValue(PasswordProperty);
        }
        public static void SetPassword(DependencyObject obj, string value)
        {
            obj.SetValue(PasswordProperty, value);
        }
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.RegisterAttached("Password", typeof(string), typeof(Extensions), new FrameworkPropertyMetadata(String.Empty, new PropertyChangedCallback(PasswordChangedCallback)));
        private static void PasswordChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var passwordBox = d as PasswordBox;
            if (passwordBox != null)
            {
                var newValue = (string)e.NewValue;
                if (passwordBox.Password != newValue)
                    passwordBox.Password = newValue;
                _attachedPasswordBoxes.RemoveAll(i => !i.IsAlive);
                if (!_attachedPasswordBoxes.Any(i => i.Target == passwordBox))
                {
                    _attachedPasswordBoxes.Add(new WeakReference(passwordBox));
                    passwordBox.PasswordChanged += SourcePasswordChanged;
                }
            }
        }
        private static void SourcePasswordChanged(object sender, EventArgs e)
        {
            Contract.Assume(_attachedPasswordBoxes.Any(i => i.Target == sender));

            var passwordBox = (PasswordBox)sender;
            if (Extensions.GetPassword(passwordBox) != passwordBox.Password)
                Extensions.SetPassword(passwordBox, passwordBox.Password);
        }
        private static readonly List<WeakReference> _attachedPasswordBoxes = new List<WeakReference>();



        public static IComparer GetComparer(DependencyObject obj)
        {
            return (IComparer)obj.GetValue(ComparerProperty);
        }
        public static void SetComparer(DependencyObject obj, IComparer value)
        {
            obj.SetValue(ComparerProperty, value);
        }
        public static readonly DependencyProperty ComparerProperty =
            DependencyProperty.RegisterAttached("Comparer", typeof(IComparer), typeof(Extensions));



        public static bool GetIsCustomSortingEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsCustomSortingEnabledProperty);
        }
        public static void SetIsCustomSortingEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(IsCustomSortingEnabledProperty, value);
        }
        public static readonly DependencyProperty IsCustomSortingEnabledProperty =
            DependencyProperty.RegisterAttached("IsCustomSortingEnabled", typeof(bool), typeof(Extensions), new PropertyMetadata(false, IsCustomSortingEnabledChangedCallback));
        private static void IsCustomSortingEnabledChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var dataGrid = (Microsoft.Windows.Controls.DataGrid)d;
            var oldValue = (bool)e.OldValue;
            var newValue = (bool)e.NewValue;
            if (!oldValue && newValue)
            {
                dataGrid.Sorting += DataGrid_Sorting;
            }
            else if (oldValue && !newValue)
            {
                dataGrid.Sorting -= DataGrid_Sorting;
            }
            else
            {
                throw new InvalidOperationException();
            }
        }
        private static void DataGrid_Sorting(object sender, Microsoft.Windows.Controls.DataGridSortingEventArgs e)
        {
            var dataGrid = (ItemsControl)sender;
            var comparer = Extensions.GetComparer(e.Column);
            ListCollectionView collectionView =
                dataGrid.ItemsSource is CollectionView ?
                dataGrid.ItemsSource as ListCollectionView :
                CollectionViewSource.GetDefaultView(dataGrid.ItemsSource) as ListCollectionView;
            if (comparer != null && collectionView != null)
            {
                if (e.Column.SortDirection != ListSortDirection.Ascending)
                {
                    collectionView.CustomSort = comparer;
                    e.Column.SortDirection = ListSortDirection.Ascending;
                }
                else
                {
                    collectionView.CustomSort = new InvertingComparer(comparer);
                    e.Column.SortDirection = ListSortDirection.Descending;
                }
                e.Handled = true;
            }
            else
            {
                e.Handled = false;
            }
        }

        
        
        public static bool GetIsFlashing(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsFlashingProperty);
        }
        public static void SetIsFlashing(DependencyObject obj, bool value)
        {
            obj.SetValue(IsFlashingProperty, value);
        }
        // Using a DependencyProperty as the backing store for IsFlashing.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsFlashingProperty =
            DependencyProperty.RegisterAttached("IsFlashing", typeof(bool), typeof(Extensions), new UIPropertyMetadata(false, IsFlashingChangedCallback));
        private static void IsFlashingChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = (Window)d;
            var oldValue = (bool)e.OldValue;
            var newValue = (bool)e.NewValue;
            if (!oldValue && newValue)
            {
                window.Activated -= Window_Activated;
                window.Activated += Window_Activated;
                FlashWindow.Start(window);
            }
            else if (oldValue && !newValue)
            {
                FlashWindow.Stop(window);
            }
        }
        private static void Window_Activated(object sender, EventArgs e)
        {
            SetIsFlashing((Window)sender, false);
        }
    }
}
